package com.legsem.jhosync.model;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;

import org.apache.commons.dbutils.ResultSetHandler;

public class MasterDoc extends Model implements ResultSetHandler < MasterDoc > {

    private static final long serialVersionUID = 1L;

    public static final String TABLE_NAME = "MASTERDOCS";

    public static final String SQL_CREATE = "CREATE TABLE "
            + TABLE_NAME
            + " (\n"
            + "         APP_NAME          VARCHAR (255),\n"
            + "         SOURCE_NAME       VARCHAR (255),\n"
            + "         USER_LOGIN        VARCHAR (255),\n"
            + "         MD                CLOB,\n"
            + "         MD_SIZE           INTEGER,\n"
            + "         REFRESH_TIME      TIMESTAMP,\n"
            + "         ERRORS            CLOB,\n"
            + "         METADATA          CLOB,\n"
            + "         METADATA_SHA1     CHAR(40),\n"
            + "         PRIMARY KEY ( APP_NAME, SOURCE_NAME, USER_LOGIN ),\n"
            + "         FOREIGN KEY ( USER_LOGIN ) REFERENCES USERS ( LOGIN ) ON DELETE CASCADE);\n";

    public static final String SQL_DROP = "DROP TABLE " + TABLE_NAME + ";\n";

    private String appName;
    private String sourceName;
    private String userLogin;
    private String md;
    private int mdSize;
    private Timestamp refreshTime;
    private String errors;
    private String metadata;
    private String metadataSha1;

    public MasterDoc() {
    }

    public MasterDoc(String appName, String sourceName, String userLogin) {
        this.appName = appName;
        this.sourceName = sourceName;
        this.userLogin = userLogin;
        refreshTime = new Timestamp(System.currentTimeMillis());
    }

    public static String getSqlCreate() {
        return SQL_CREATE;
    }

    public static String getSqlDrop() {
        return SQL_DROP;
    }

    public static MasterDoc find(String appName, String sourceName,
            String userLogin) throws SQLException {
        return find(appName, sourceName, userLogin, false);
    }

    public static MasterDoc findForUpdate(String appName, String sourceName,
            String userLogin) throws SQLException {
        return find(appName, sourceName, userLogin, true);
    }

    public static MasterDoc find(String appName, String sourceName,
            String userLogin, boolean forUpdate) throws SQLException {
        return run.query(getConnection(), "SELECT * FROM " + TABLE_NAME + ""
                + " WHERE APP_NAME=? AND SOURCE_NAME=? AND USER_LOGIN=?"
                + ((forUpdate) ? " FOR UPDATE" : ""), new MasterDoc(), appName,
                sourceName, userLogin);
    }

    public int create() throws SQLException {
        return run.update(getConnection(), "INSERT INTO " + TABLE_NAME + ""
                + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", appName, sourceName,
                userLogin, md, mdSize, refreshTime, errors, metadata,
                metadataSha1);
    }

    public int delete() throws SQLException {
        return run.update(getConnection(), "DELETE FROM " + TABLE_NAME + ""
                + " WHERE APP_NAME=? AND SOURCE_NAME=? AND USER_LOGIN=?",
                appName, sourceName, userLogin);
    }

    public int update() throws SQLException {
        return run.update(getConnection(), "UPDATE " + TABLE_NAME + " SET"
                + " MD = ?," + " MD_SIZE = ?," + " REFRESH_TIME = ?,"
                + " ERRORS = ?, METADATA = ?, METADATA_SHA1 = ?"
                + " WHERE  APP_NAME=? AND SOURCE_NAME=? AND USER_LOGIN=?", md,
                mdSize, refreshTime, errors, metadata, metadataSha1, appName,
                sourceName, userLogin);
    }

    /**
     * Determine if document needs to be refreshed from the backend system.
     * <p/>
     * A new document (not yet stored) always need refresh.
     * <p/>
     * Otherwise if scheduled scheduled refresh time is passed, then return
     * true.
     * <p/>
     * TODO see how expensive is the refresh. we might need a separate ReadState
     * table like rhosync.
     * 
     * @return true if data needs to be refreshed
     */
    public boolean isNeedRefresh() throws SQLException {
        if (find(appName, sourceName, userLogin) == null) {
            return true;
        } else {
            if (getRefreshTime().compareTo(
                    new Timestamp(System.currentTimeMillis())) < 0) {
                return true;
            }
            return false;
        }
    }

    public String getMd() {
        return md;
    }

    public void setMd(String cd) {
        this.md = cd;
    }

    public int getMdSize() {
        return mdSize;
    }

    public void setMdSize(int mdSize) {
        this.mdSize = mdSize;
    }

    public Timestamp getRefreshTime() {
        return refreshTime;
    }

    public void setRefreshTime(Timestamp refreshTime) {
        this.refreshTime = refreshTime;
    }

    public String getErrors() {
        return errors;
    }

    public void setErrors(String errors) {
        this.errors = errors;
    }

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public String getSourceName() {
        return sourceName;
    }

    public void setSourceName(String sourceName) {
        this.sourceName = sourceName;
    }

    public String getUserLogin() {
        return userLogin;
    }

    public void setUserLogin(String userLogin) {
        this.userLogin = userLogin;
    }

    public String getMetadata() {
        return metadata;
    }

    public void setMetadata(String metadata) {
        this.metadata = metadata;
    }

    public String getMetadataSha1() {
        return metadataSha1;
    }

    public void setMetadataSha1(String metadataSha1) {
        this.metadataSha1 = metadataSha1;
    }

    public MasterDoc handle(ResultSet rs) throws SQLException {
        if (!rs.next()) {
            return null;
        }
        MasterDoc masterDoc = new MasterDoc();
        ResultSetMetaData meta = rs.getMetaData();
        int cols = meta.getColumnCount();

        for (int i = 0; i < cols; i++) {
            switch (i) {
            case 0:
                masterDoc.setAppName(rs.getString(i + 1));
                break;
            case 1:
                masterDoc.setSourceName(rs.getString(i + 1));
                break;
            case 2:
                masterDoc.setUserLogin(rs.getString(i + 1));
                break;
            case 3:
                masterDoc.setMd(rs.getString(i + 1));
                break;
            case 4:
                masterDoc.setMdSize(rs.getInt(i + 1));
                break;
            case 5:
                masterDoc.setRefreshTime(rs.getTimestamp(i + 1));
                break;
            case 6:
                masterDoc.setErrors(rs.getString(i + 1));
                break;
            case 7:
                masterDoc.setMetadata(rs.getString(i + 1));
                break;
            case 8:
                masterDoc.setMetadataSha1(rs.getString(i + 1));
                break;
            }
        }

        return masterDoc;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((appName == null) ? 0 : appName.hashCode());
        result = prime * result
                + ((sourceName == null) ? 0 : sourceName.hashCode());
        result = prime * result
                + ((userLogin == null) ? 0 : userLogin.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        MasterDoc other = (MasterDoc) obj;
        if (appName == null) {
            if (other.appName != null) {
                return false;
            }
        } else if (!appName.equals(other.appName)) {
            return false;
        }
        if (sourceName == null) {
            if (other.sourceName != null) {
                return false;
            }
        } else if (!sourceName.equals(other.sourceName)) {
            return false;
        }
        if (userLogin == null) {
            if (other.userLogin != null) {
                return false;
            }
        } else if (!userLogin.equals(other.userLogin)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "MasterDoc [appName=" + appName + ", sourceName=" + sourceName
                + ", userLogin=" + userLogin + ", md=" + toShortString(md)
                + ", mdSize=" + mdSize + ", refreshTime=" + refreshTime
                + ", errors=" + errors + ", metadata=" + metadata
                + ", metadataSha1=" + metadataSha1 + "]";
    }

}
